﻿
using System;
using System.Collections.Generic;
using System.Text;

namespace SharpSoft.Geometries
{
    using SharpSoft.Geometries.Shapes;
    /// <summary>
    /// 向量
    /// </summary>
    public struct Vector2D
    {
        public Vector2D(double x, double y)
        {
            this._x = x; this._y = y;
        }
        /// <summary>
        /// 初始化点p1到p2的向量
        /// </summary>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        public Vector2D(Point2D p1, Point2D p2)
        {
            this._x = p2._x - p1._x;
            this._y = p2._y - p1.Y;
        }

        internal double _x, _y;
        public double X { get => _x; set => _x = value; }

        public double Y { get => _y; set => _y = value; }
        /// <summary>
        /// 获取向量的长度
        /// </summary>
        public double Length => Math.Sqrt(X * X + Y * Y);
        /// <summary>
        /// 获取向量的平方和
        /// </summary>
        public double LengthSquared => X * X + Y * Y;
        /// <summary>
        /// 增加向量的长度
        /// </summary>
        public void AddLength(double length_incr)
        {
            var l = this.Length;
            if (l == 0)
            {
                throw new Exception("无方向的向量不能更改其长度。");
            }
            var nl = l + length_incr;
            var b = nl / l;
            this._x = b * this._x;
            this._y = b * this._y;
        }

        public override string ToString()
        {
            return $"[{this.X:F2},{this.Y:F2}]";
        }

        #region 运算
        public static bool operator ==(Vector2D vector1, Vector2D vector2)
        {
            if (vector1.X == vector2.X)
            {
                return vector1.Y == vector2.Y;
            }
            return false;
        }

        public static bool operator !=(Vector2D vector1, Vector2D vector2)
        {
            return !(vector1 == vector2);
        }

        public static bool Equals(Vector2D vector1, Vector2D vector2)
        {
            double num = vector1.X;
            if (num.Equals(vector2.X))
            {
                num = vector1.Y;
                return num.Equals(vector2.Y);
            }
            return false;
        }

        public override bool Equals(object o)
        {
            if (o != null && o is Vector2D)
            {
                Vector2D vector = (Vector2D)o;
                return Equals(this, vector);
            }
            return false;
        }

        public bool Equals(Vector2D value)
        {
            return Equals(this, value);
        }

        public override int GetHashCode()
        {
            double num = X;
            int hashCode = num.GetHashCode();
            num = Y;
            return hashCode ^ num.GetHashCode();
        }

        /// <summary>
        /// 规范化此向量
        /// </summary>
        public void Normalize()
        {
            this /= Math.Max(Math.Abs(_x), Math.Abs(_y));
            this /= Length;
        }
        /// <summary>
        /// 获取v2在v1上的投影向量
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        public static Vector2D Projection(Vector2D v1, Vector2D v2)
        {
            var v1l = v1.Length;
            var l = v1 * v2 / v1l;
            v1.AddLength(l - v1l);
            return v1;
        }

        /// <summary>
        /// 计算两个向量的叉积
        /// </summary>
        /// <param name="vector1"></param>
        /// <param name="vector2"></param>
        /// <returns></returns>
        public static double CrossProduct(Vector2D vector1, Vector2D vector2)
        {
            return vector1._x * vector2._y - vector1._y * vector2._x;
        }
        /// <summary>
        /// 向量点乘
        /// </summary>
        /// <param name="vector1"></param>
        /// <param name="vector2"></param>
        /// <returns></returns>
        public static double DotProduct(Vector2D vector1, Vector2D vector2)
        {
            return Multiply(vector1, vector2);
        }
        /// <summary>
        /// 向量点乘
        /// </summary>
        /// <param name="vector2"></param>
        /// <returns></returns>
        public double DotProduct(Vector2D vector2)
        {
            return Multiply(this, vector2);
        }
        /// <summary>
        /// 获取两个向量的转向角（在笛卡尔坐标系中X轴正方向往Y轴正方向旋转为正向旋转（逆时针），往Y轴负方向旋转为逆向旋转（顺时针）。）
        /// </summary>
        /// <param name="vector1"></param>
        /// <param name="vector2"></param>
        /// <returns></returns>
        public static Radian AngleBetween(Vector2D vector1, Vector2D vector2)
        {
            double y = vector1._x * vector2._y - vector2._x * vector1._y;
            double x = vector1._x * vector2._x + vector1._y * vector2._y;
            return (Radian)Math.Atan2(y, x);
        }

        /// <summary>
        /// 对此向量求反。 向量具有相同的量级为之前，但其方向现在相反。
        /// </summary>
        public void Negate()
        {
            _x = 0.0 - _x;
            _y = 0.0 - _y;
        }

        public static Vector2D operator -(Vector2D vector)
        {
            return new Vector2D(0.0 - vector._x, 0.0 - vector._y);
        }
        public static Vector2D operator +(Vector2D vector1, Vector2D vector2)
        {
            return new Vector2D(vector1._x + vector2._x, vector1._y + vector2._y);
        }

        public static Vector2D Add(Vector2D vector1, Vector2D vector2)
        {
            return new Vector2D(vector1._x + vector2._x, vector1._y + vector2._y);
        }

        public static Vector2D operator -(Vector2D vector1, Vector2D vector2)
        {
            return new Vector2D(vector1._x - vector2._x, vector1._y - vector2._y);
        }

        public static Vector2D Subtract(Vector2D vector1, Vector2D vector2)
        {
            return new Vector2D(vector1._x - vector2._x, vector1._y - vector2._y);
        }

        public static Point2D operator +(Vector2D vector, Point2D point)
        {
            return new Point2D(point.X + vector._x, point.Y + vector._y);
        }

        public static Point2D Add(Vector2D vector, Point2D point)
        {
            return new Point2D(point.X + vector._x, point.Y + vector._y);
        }
        /// <summary>
        /// 缩放向量
        /// </summary>
        /// <param name="vector"></param>
        /// <param name="scalar"></param>
        /// <returns></returns>
        public static Vector2D operator *(Vector2D vector, double scalar)
        {
            return new Vector2D(vector._x * scalar, vector._y * scalar);
        }

        public static Vector2D Multiply(Vector2D vector, double scalar)
        {
            return new Vector2D(vector._x * scalar, vector._y * scalar);
        }
        /// <summary>
        /// 缩放向量
        /// </summary>
        /// <param name="scalar"></param>
        /// <param name="vector"></param>
        /// <returns></returns>
        public static Vector2D operator *(double scalar, Vector2D vector)
        {
            return new Vector2D(vector._x * scalar, vector._y * scalar);
        }
        /// <summary>
        /// 缩放向量
        /// </summary>
        /// <param name="scalar"></param>
        /// <param name="vector"></param>
        /// <returns></returns>
        public static Vector2D Multiply(double scalar, Vector2D vector)
        {
            return new Vector2D(vector._x * scalar, vector._y * scalar);
        }
        /// <summary>
        /// 缩放向量
        /// </summary>
        /// <param name="vector"></param>
        /// <param name="scalar"></param>
        /// <returns></returns>
        public static Vector2D operator /(Vector2D vector, double scalar)
        {
            return vector * (1.0 / scalar);
        }
        /// <summary>
        /// 缩放向量
        /// </summary>
        /// <param name="vector"></param>
        /// <param name="scalar"></param>
        /// <returns></returns>
        public static Vector2D Divide(Vector2D vector, double scalar)
        {
            return vector * (1.0 / scalar);
        }

        public static Vector2D operator *(Vector2D vector, Transform2D matrix)
        {
            return matrix.Transform(vector);
        }

        public static Vector2D Multiply(Vector2D vector, Transform2D matrix)
        {
            return matrix.Transform(vector);
        }
        /// <summary>
        /// 向量点乘
        /// </summary>
        /// <param name="vector1"></param>
        /// <param name="vector2"></param>
        /// <returns></returns>
        public static double operator *(Vector2D vector1, Vector2D vector2)
        {
            return vector1._x * vector2._x + vector1._y * vector2._y;
        }

        /// <summary>
        /// 向量点乘
        /// </summary>
        /// <param name="vector1"></param>
        /// <param name="vector2"></param>
        /// <returns></returns>
        public static double Multiply(Vector2D vector1, Vector2D vector2)
        {
            return vector1._x * vector2._x + vector1._y * vector2._y;
        }
        /// <summary>
        /// 计算两个向量的行列式
        /// </summary>
        /// <param name="vector1"></param>
        /// <param name="vector2"></param>
        /// <returns></returns>
        public static double Determinant(Vector2D vector1, Vector2D vector2)
        {
            return vector1._x * vector2._y - vector1._y * vector2._x;
        }

        public static explicit operator Size2D(Vector2D vector)
        {
            return new Size2D(Math.Abs(vector._x), Math.Abs(vector._y));
        }

        public static explicit operator Point2D(Vector2D vector)
        {
            return new Point2D(vector._x, vector._y);
        }


        #endregion

        #region 变换
        /// <summary>
        /// 将当前向量绕向量起点旋转指定的角度得到一个新的向量
        /// </summary>
        /// <param name="radian"></param>
        /// <returns></returns>
        public Vector2D Rotate(Radian radian)
        {
            /*
 x1 = x0 * cos（-B） - y0 * sin（-B） =>  x1 = x0 * cosB + y0 * sinB
 y1 = x0 * sin（-B） + y0 * cos（-B）=>  y1 = -x0 * sinB + y0 * cosB
             */
            var x1 = this.X * Math.Cos(radian) + this.Y * Math.Sin(radian);
            var y1 = -this.X * Math.Sin(radian) + this.Y * Math.Cos(radian);
            return new Vector2D(x1, y1);
        }



        #endregion
    }
}
